#ifndef _LOG_HPP_
#define _LOG_HPP_

#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <memory>
#include <unistd.h>
#include <ctime>
#include <cstdio>
#include "Mutex.hpp"

namespace LogModule
{
    using namespace MutexModule;

    const std::string gsep = "\r\n";
    // 刷新策略基类
    class LogStrategy
    {
    public:
        ~LogStrategy() = default;
        virtual void SyncLog(const std::string &message) = 0;
    };

    // 显示器打印日志的策略
    class ConsoleLogstrategy : public LogStrategy
    {
    public:
        ConsoleLogstrategy() {}
        virtual void SyncLog(const std::string &message) override
        {
            MutexGuard lockguard(&_mutex);
            std::cout << message << gsep;
        }
        ~ConsoleLogstrategy() {}

    private:
        Mutex _mutex;
    };

    // 文件打印日志的策略
    const std::string defaultpath = "/var/log";
    const std::string defaultfile = "my.log";

    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
            : _path(path),
              _file(file)
        {
            if (std::filesystem::exists(_path)) // 如果_path存在
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path); // 如果创建失败了，try catch捕捉一下
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        virtual void SyncLog(const std::string &message) override
        {
            MutexGuard lockguard(&_mutex);
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream out(filename, std::ios::app); // 追加写入的方式打开
            if (!out.is_open())
            {
                return;
            }
            out << message << gsep;
            out.close();
        }
        ~FileLogStrategy() {}

    private:
        std::string _path; // 需要写的日志的路径
        std::string _file; // 需要写的文件

        Mutex _mutex;
    };

    // 选择不同的刷新方式
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARING,
        ERROR,
        FATAL,
    };

    std::string LevelToString(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARING:
            return "WARING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }
    std::string GetCurTime()
    {
        time_t cur = time(nullptr);
        struct tm cur_tm;
        localtime_r(&cur, &cur_tm);
        char buffertime[64];
        snprintf(buffertime, sizeof(buffertime), "%4d-%02d-%d %d:%d:%d",
                 cur_tm.tm_year + 1900,
                 cur_tm.tm_mon + 1,
                 cur_tm.tm_mday,
                 cur_tm.tm_hour,
                 cur_tm.tm_min,
                 cur_tm.tm_sec);
        return buffertime;
    }
    // 1.形成日志
    // 2.根据不同的策略，完成刷新
    class Logger
    {
    public:
        Logger()
        {
            EnableConsoleLogStrtegy(); // 默认使用显示器文件打印
        }

        void EnableFileLogStrategy()
        {
            _fflush_strategy = std::make_unique<FileLogStrategy>();
        }
        void EnableConsoleLogStrtegy()
        {
            _fflush_strategy = std::make_unique<ConsoleLogstrategy>();
        }

        // 内部类
        // 表示的是未来的一条日志
        class LogMessage
        {
        public:
            // 左半部分
            LogMessage(LogLevel &level, std::string &src_name, int line_number, Logger &logger)
                : _cur_time(GetCurTime()),
                  _level(level),
                  _pid(getpid()),
                  _src_name(src_name),
                  _line_number(line_number),
                  _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _cur_time << "] "
                   << "[" << LevelToString(_level) << "] " // 因为是自定义的类，无法转换，这里要转化一下
                   << "[" << _pid << "] "
                   << "[" << _src_name << "] "
                   << "[" << _line_number << "] "
                   << "- ";
                _loginfo = ss.str();
            }

            // 右半部分
            template <class T>
            LogMessage &operator<<(const T &info)
            {
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }
            ~LogMessage()
            {
                // 内部类可以访问外部类的私有成员
                if (_logger._fflush_strategy) // 刷新策略不为空
                {
                    _logger._fflush_strategy->SyncLog(_loginfo);
                }
            }

        private:
            std::string _cur_time;
            LogLevel _level;
            pid_t _pid;
            std::string _src_name;
            int _line_number;
            std::string _loginfo;
            Logger &_logger;
        };
        // 故意写成返回临时对象
        // 为了实现LogMessage << 1 << 23.12 << "hello"
        // 一直到运算符结束才会调用析构
        LogMessage operator()(LogLevel level, std::string name, int number)
        {
            return LogMessage(level, name, number, *this);
        }
        ~Logger() {}

    private:
        std::unique_ptr<LogStrategy> _fflush_strategy;
    };
    // 全局日志对象
    Logger logger;
// log(LogLevel::DEBUG,main.cc,10)<<"hello world";

// 用宏来简化用户操作，获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strtegy() logger.EnableConsoleLogStrtegy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}

#endif